package com.zlyx.easy.core.utils;

import java.lang.annotation.Annotation;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ApplicationEvent;

import com.zlyx.easy.core.annotations.SpringBean;
import com.zlyx.easy.core.map.Maps;

/**
 * @Auth 赵光
 * @Describle
 * @2018年12月27日 上午9:17:57
 */
@SpringBean(todo = { "上下文工具" })
public class SpringUtils implements ApplicationContextAware {

	protected static ApplicationContext applicationContext;

	@Override
	public void setApplicationContext(ApplicationContext context) {
		SpringUtils.applicationContext = context;
	}

	public static ApplicationContext getApplicationContext() {
		return SpringUtils.applicationContext;
	}

	public static void publish(ApplicationEvent event) {
		if (SpringUtils.getApplicationContext() != null) {
			SpringUtils.getApplicationContext().publishEvent(event);
		}
	}

	@SuppressWarnings("unchecked")
	public static <T> T getBean(String beanName) {
		if (ObjectUtils.isEmpty(beanName)) {
			LogUtils.err(SpringUtils.class, "beanName cannot been null or \"\"");
			return null;
		}
		return (T) SpringUtils.getApplicationContext().getBean(beanName);
	}

	public static boolean haveBean(String beanName) {
		return SpringUtils.getApplicationContext().containsBean(beanName);
	}

	public static Class<?> getBeanClass(String beanName) {
		return getBean(beanName).getClass();
	}

	public static Set<String> getBeanNamesByAnnotation(Class<? extends Annotation> annotation) {
		if (annotation == null) {
			annotation = Annotation.class;
		}
		return SpringUtils.getApplicationContext().getBeansWithAnnotation(annotation).keySet();
	}

	public static Map<String, Object> getBeanswithAnnotation(Class<? extends Annotation> annotation) {
		if (annotation == null) {
			annotation = Annotation.class;
		}
		return SpringUtils.getApplicationContext().getBeansWithAnnotation(annotation);
	}

	public static Collection<Object> getBeansByAnnotation(Class<? extends Annotation> annotation) {
		return SpringUtils.getBeanswithAnnotation(annotation).values();
	}

	public static String getClassNameByBeanName(String beanName) {
		return SpringUtils.getApplicationContext().getBean(beanName).getClass().getSimpleName().toLowerCase();
	}

	public static <T> Map<String, T> getBeansOfType(Class<T> beanSuperClass) {
		return SpringUtils.getApplicationContext().getBeansOfType(beanSuperClass);
	}

	@SuppressWarnings("unchecked")
	public static <T> Map<Class<?>, Object> getBeanMapByCls(Class<?> cls) throws Exception {
		Map<Class<?>, Object> beanMap = Maps.newMap();
		Map<String, Object> beans = null;
		Object target = null;
		Class<?> targetClass = null;
		if (cls.isAnnotation()) {
			beans = getBeanswithAnnotation((Class<? extends Annotation>) cls);
		} else {
			beans = (Map<String, Object>) getBeansOfType(cls);
		}
		if (beans == null || beans.isEmpty()) {
			return beanMap;
		}
		for (Entry<String, ?> entry : beans.entrySet()) {
			target = ProxyUtils.getTarget(entry.getValue());
			targetClass = target.getClass();
			if (target instanceof Proxy) {
				targetClass = entry.getValue().getClass().getInterfaces()[0];
				beanMap.put(targetClass, target);
			} else if (targetClass.getInterfaces().length > 0) {
				beanMap.put(targetClass.getInterfaces()[0], target);
			} else {
				beanMap.put(targetClass, target);
			}
		}
		return beanMap;
	}

	public static <T> List<T> getBeansByType(Class<T> beanSuperClass) {
		Map<String, T> beans = SpringUtils.getApplicationContext().getBeansOfType(beanSuperClass);
		if (beans.size() > 0) {
			return new ArrayList<T>(beans.values());
		}
		return null;
	}

	public static <T> T getBeanByType(Class<T> beanSuperClass) {
		Map<String, T> beans = SpringUtils.getApplicationContext().getBeansOfType(beanSuperClass);
		if (beans.size() == 0) {
			return new ArrayList<T>(beans.values()).get(0);
		}
		return null;
	}
}
